package gbench.sandbox.data.index;

import gbench.common.matlib.algebra.lisp.DFrame;
import gbench.common.matlib.algebra.lisp.IRecord;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import gbench.common.fs.XlsFile.SimpleExcel;

/**
 * 
 * @author Administrator
 *
 */
public class TableSearchEngine extends AbstractTableSearchEngine {

    /**
     * 
     * @param keyTableBindings
     * @return
     */
    public SearchSession getSession(Algorithm algorithm, String... keyTableBindings) {
        final var session = new SearchSession(algorithm, keyTableBindings);
        return session;
    }

    /**
     * 会话操作
     * 
     * @param <T>            返回值类型
     * @param algorithm      索引算法
     * @param action         回调函数
     * @param keyTblBindings 键表绑定
     * @return T类型的结果
     */
    public <T> T withTransaction(final Function<SearchSession, T> action, Algorithm algorithm,
            final String... keyTblBindings) {
        final var session = new SearchSession(algorithm, keyTblBindings);
        return session.withTransaction(action);
    }

    /**
     * 会话操作
     * 
     * @param <T>            返回值类型
     * @param keyTblBindings 键表绑定
     * @return T类型的结果
     */
    public <T> T withTransaction(final Function<SearchSession, T> action, final String... keyTblBindings) {
        return this.withTransaction(action, Algorithm.KEY_VALUE, keyTblBindings);
    }

    /**
     * 使用 id 索引进行数据检索. <br>
     * 实际使用的 索引文件为 tablen的id文件 即 indexStore
     * 
     * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
     * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
     * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
     * 
     * @param keyTblBindings 键名表 绑定序列:key1,table1,key2,table2,....
     * @return encoder 函数
     *         keywords(key1:keyword1,key2,keyword2)->rec(key1:索引keyword1的结果,key1:索引keyword2的结果)
     */
    @Override
    public Function<IRecord, IRecord> getEncoder(final String... keyTblBindings) {
        return keywords -> this.withTransaction(sess -> sess.encode(keywords), keyTblBindings);
    }

    /**
     * 使用 id 索引进行数据检索. <br>
     * 实际使用的 索引文件为 tablen的id文件 即 indexStore <br>
     * 
     * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
     * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
     * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
     * 
     * @param keyTblBindings 键表绑定序列
     * @param hit_mapper     值变换函数,命中数据即hit的变换函数
     * @return encoder 函数
     *         keywords(key1:keyword1,key2,keyword2)->rec(key1:索引keyword1的结果,key1:索引keyword2的结果)
     */
    @Override
    public Function<IRecord, IRecord> getEncoder(final Function<IRecord, Object> hit_mapper,
            final String... keyTblBindings) {
        return keywords -> this.withTransaction(sess -> sess.encode(keywords, hit_mapper), keyTblBindings);
    }

    /**
     * 使用 name 索引进行数据检索. <br>
     * 实际使用的 索引文件为 tablen的name索引文件 即 indexStore
     * 
     * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
     * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
     * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
     * 
     * @param keyTblBindings 键表绑定序列
     * @return decoder 函数
     *         keywords(key1:keyword1,key2,keyword2)->rec(key1:索引keyword1的结果,key1:索引keyword2的结果)
     */
    @Override
    public Function<IRecord, IRecord> getDecoder(final String... keyTblBindings) {
        return keywords -> this.withTransaction(sess -> sess.decode(keywords), keyTblBindings);
    }

    /**
     * 使用 name 索引进行数据检索. <br>
     * 实际使用的 索引文件为 tablen的name索引文件 即 indexStore <br>
     * 
     * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
     * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
     * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
     * 
     * @param mapper         数据映射
     * @param keyTblBindings 键表绑定序列
     * @param hit_mapper     值变换函数,命中数据即hit的变换函数
     * @return decoder 函数
     *         keywords(key1:keyword1,key2,keyword2)->rec(key1:索引keyword1的结果,key1:索引keyword2的结果)
     */
    @Override
    public Function<IRecord, IRecord> getDecoder(final Function<IRecord, Object> mapper,
            final String... keyTblBindings) {
        return keywords -> this.withTransaction(sess -> sess.decode(keywords, mapper), keyTblBindings);
    }

    /**
     * 创建缓存
     * 
     * @param entityId  主体id
     * @param excelfile 数据文件
     * @param tables    数据表序列
     * @return 数据缓存
     */
    public static TableSearchEngine of(final String entityId, final String excelfile, final String... tables) {
        return TableSearchEngine.of(entityId, excelfile, new Algorithm[] { Algorithm.KEY_VALUE },
                (alogrithm, dataItem) -> "id,name".split(","), tables);
    }

    /**
     * 创建缓存
     * 
     * @param entityId  主体id
     * @param excelfile 数据文件
     * @param tables    数据表序列
     * @return 数据缓存
     */
    public static TableSearchEngine of(final String entityId, final String excelfile, Algorithm[] algorithmIds,
            BiFunction<Algorithm, IRecord, String[]> index_creator, final String... tables) {

        final var srchEngine = new TableSearchEngine();
        final var excel = SimpleExcel.of(excelfile);

        for (final var table : tables) { // 表数据索引化，一张表一组索引
            final var dfm = excel.autoDetect(table).collect(DFrame.dfmclc2);
            for (var algorithmId : algorithmIds) {
                final var tableAlgorithm = srchEngine.addAlgorithm(algorithmId);
                final var indexEngine = tableAlgorithm.addIndexEngine(table, index_creator);
                final var dataItems = dfm.rowS().collect(Collectors.toList());
                indexEngine.handle(dataItems);
            } // for
        } // for

        return srchEngine;
    }

    /**
     * Key table binding 的 生成函数
     * 
     * key 用来 标记 关键字位置 key 的值 就是 keyword <br>
     * table 用来标记 索引位置 table 的数据 就是 index 数据索引，id索引，name索引<br>
     * 每个索引都是 一个 UUID,用来 指向 一个 完整的 数据记录IRecord <br>
     * 
     * @param line 绑定描述<br>
     *             1) 序列模式 key1,table1,key2,table2,... <br>
     *             1) 包含模式 key11,key12:table1;key21,key22:table2;... <br>
     * @return 数据的绑定模式
     */
    public static String[] KTB(final String line) {
        final var ktb_bindings = new ArrayList<String>();
        final var _lines = line.split("[;]+");
        final var pattern = Pattern.compile("^([^:]+):([^:]+)$");
        final Function<String, String[]> split = ln -> ln.split("[,\\s]+");

        for (var _line : _lines) {
            if (line.matches("^\\s*$")) { // 过滤空行
                continue;
            }
            final var matcher = pattern.matcher(_line);
            if (matcher.matches()) { // 包含模式
                final var p1 = matcher.group(1);
                final var p2 = matcher.group(2);
                for (var u : split.apply(p1)) {
                    for (var v : split.apply(p2)) {
                        ktb_bindings.addAll(Arrays.asList(u, v));
                    } // for
                } // for
            } else { // 序列模式
                ktb_bindings.addAll(Arrays.asList(split.apply(_line)));
            } // if
        } // for

        return ktb_bindings.stream().map(e -> e.strip()).toArray(String[]::new);
    }

}
